import pygame

import random

#
# 游戏窗口区域（矩形区域）
SCREEN_RECT = pygame.Rect(0, 0, 480, 700)

FRAME_INTERVAL = 10

HERO_BOMB_COUNT = 3
# # 英雄飞机默认初始位置
HERO_DEFAULT_MTD_BOTTOM = (SCREEN_RECT.centerx, SCREEN_RECT.bottom - 90)

HERO_DEAD_EVENT = pygame.USEREVENT  # 英雄飞机牺牲事件
HERO_POWER_OFF_EVENT = pygame.USEREVENT + 1


#
#
class GameSprite(pygame.sprite.Sprite):
    """游戏精灵类"""
    image_path = "E:/box\start\课程设计——飞机大战\image"

    def __init__(self, image_name, speed, *groups):
        """构造方法
        :param image_name: 要加载的图片文件名
        :param speed: 移动速度，0表示静止
        :param groups: 要添加到精灵组，不传值则不添加
        """
        super().__init__(*groups)
        # 图像
        self.image = pygame.image.load(self.image_path + '/' + image_name)
        self.rect = self.image.get_rect()  # 矩形区域，默认在左上角
        self.speed = speed
        # 图像遮掩，可以提高碰撞测试的执行性
        self.mask = pygame.mask.from_surface(self.image)

    def update(self, *args):
        """更新精灵位置，默认在垂直方向移动"""
        self.rect.y += self.speed


#
#
class Background(GameSprite):
    """背景精灵类"""

    def __init__(self, is_alt, *groups):
        # 调用父类方法实现精灵的创建
        super().__init__("/background.png", 1, *groups)
        # 判断是否是另一个精灵，如果是，需要设置初始位置
        if is_alt:
            self.rect.y = -self.rect.h  # 设置到游戏窗口正上方

    def update(self, *args):
        # 调用父类的方法实现向下运动
        super().update(*args)  # 向下运动
        # 判断是否移出屏幕，如果移出屏幕，将图像设置到屏幕的上方
        if self.rect.y >= self.rect.h:
            self.rect.y = -self.rect.h


#
#
class StatusButton(GameSprite):
    """状态按钮类"""

    def __init__(self, image_names, *groups):
        """构造方法
        :param image_names:要加载的图像名称列表
        :param groups:要添加到的精灵组
        """
        super().__init__(image_names[0], 0, *groups)
        # 加载图像
        self.images = [pygame.image.load(self.image_path + name) for name in image_names]

    def switch_status(self, is_pause):
        """切换状态
        :param is_pause:是否暂停
        """
        self.image = self.images[1 if is_pause else 0]


#
#
class Label(pygame.sprite.Sprite):
    """文本标签精灵"""
    font_path = "E:/box\start\课程设计——飞机大战\image\仿宋_GB2312.ttf"  # 字体文件路径

    def __init__(self, text, size, color, *groups):
        """构造方法
        :param text:文本内容
        :param size: 字体大小
        :param color: 字体颜色
        :param group: 要添加到的精灵组
        """
        super().__init__(*groups)
        self.font = pygame.font.Font(self.font_path, size)
        self.color = color
        self.image = self.font.render(text, True, self.color)
        self.rect = self.image.get_rect()

    def set_text(self, text):
        """设置文本，使用指定的文本重新渲染image，并且更新rect
        :param text:文本内容
        """
        self.image = self.font.render(text, True, self.color)
        self.rect = self.image.get_rect()


#
#
class Plane(GameSprite):
    """ 飞机类 """

    def __init__(self, hp, speed, value, wav_name, normal_names, hurt_name, destroy_names, *group):
        """构造方法
        :param normal_names:记录正常飞机状态的图像名称列表
        :param group: 要添加到的精灵组
        """
        super().__init__(normal_names[0], 0, *group)
        # 飞机属性
        self.hp = hp
        self.max_hp = hp
        self.value = value
        self.wav_name = wav_name

        # 加载图像列表
        self.normal_images = [pygame.image.load(self.image_path + name) for name in normal_names]
        self.normal_index = 0
        # 受伤图像
        self.hurt_image = pygame.image.load(self.image_path + "\\" + hurt_name)

        # 被摧毁图像列表及索引
        self.destroy_images = [pygame.image.load(self.image_path + name) for name in destroy_names]
        self.destroy_index = 0

    def update(self, *args):
        # 如果第0个参数为False，不需要更新图像直接返回
        # if not args[0]:
        #     return
        # 设置图像
        # self.image = self.normal_images[self.normal_index]
        # count = len(self.normal_images)
        # self.normal_index = (self.normal_index + 1) % count
        """ 更新图像和位置 """
        # 调用父类方法更新敌机图像-注意args需要拆包
        super().update(*args)
        # 判断敌机是否飞被摧毁，若没有，则根据速度更新敌机的位置
        if self.hp > 0:
            self.rect.y += self.speed
        # 判断是否飞出屏幕，如果是，重置敌机
        if self.rect.y >= SCREEN_RECT.h:
            self.reset_plane()
        # # 如果第1个参数为False，不需要更新图像直接返回
        # # if not args[0]:
        # #     return
        # 判断飞机状态
        if self.hp == self.max_hp:
            self.image = self.normal_images[self.normal_index]  # 未受伤
            count = len(self.normal_images)
            self.normal_index = (self.normal_index + 1) % count
        elif self.hp > 0:
            self.image = self.hurt_image  # 受伤
        else:
            # 判断是否显示到最后一张图像，若是说明飞机完全被摧毁
            if self.destroy_index < len(self.destroy_images):
                self.image = self.destroy_images[self.destroy_index]
                self.destroy_index += 1
            else:
                self.reset_plane()  # 重置飞机

    def reset_plane(self):
        """重置飞机"""
        self.hp = self.max_hp
        self.normal_index = 0
        self.destroy_index = 0
        self.image = self.normal_images[0]


#
#
class Enemy(Plane):
    """ 敌机类 """

    def __init__(self, kind, max_speed, *groups):
        # 记录敌机类型和最大速度
        self.kind = kind
        self.max_speed = max_speed
        # 根据类型调用父类方法的不同参数
        if kind == 0:
            super().__init__(1, 1, 1000, "/enemy0.png",
                             ["/enemy-1.gif"], "/enemy-1.gif",
                             ["/enemy0_down%d.png" % i for i in range(1, 5)], *groups)
        elif kind == 1:
            super().__init__(6, 1, 6000, "/enemy1.png",
                             ["/enemy-2.gif"], "/enemy-2.gif",
                             ["/enemy1_down%d.png" % i for i in range(1, 5)], *groups)
        else:
            super().__init__(15, 1, 15000, "/enemy2_n2.png",
                             ["/enemy2.png", "/enemy-3.gif"],
                             "enemy2_hit.png",
                             ["/enemy2_down%d.png" % i for i in range(1, 7)], *groups)
        self.reset_plane()

    def reset_plane(self):
        """ 重置飞机 """
        super().reset_plane()
        # 设置初始随机位置和速度
        # 设置随机x值
        x = random.randint(0, SCREEN_RECT.w - self.rect.w)
        # 设置随机y值
        y = random.randint(0, SCREEN_RECT.h - self.rect.h)
        self.rect.topleft = (x, y)
        # 设置初始速度
        self.speed = random.randint(1, self.max_speed)


#
#
class Hero(Plane):
    """ 英雄飞机类 """

    def __init__(self, *groups):
        """ 构造方法
        :param groups:要添加到的精灵组
        """
        super().__init__(1000, 5, 0, "/hero.gif",
                         ["/bullet%d.png" % i for i in range(1, 3)], "/bullet.png",
                         ["/bullet-%d.gif" % i for i in range(1, 4)], *groups)
        self.is_power = False
        self.bomb_count = HERO_BOMB_COUNT
        self.bullets_kind = 0
        self.bullets_group = pygame.sprite.Group()
        # 初始位置
        self.rect.midbottom = HERO_DEFAULT_MTD_BOTTOM

    def update(self, *args):
        """ 更新英雄飞机的图像及矩形区域
        :param args:0更新图像标记，1水平移动基数，2垂直移动基数
        """
        # 调用父类方法更新飞机图像 - 注意 args 需要解包
        super().update(*args)
        # 如果没有传递方向基数或者英雄飞机被摧毁，直接返回
        if len(args) != 3 or self.hp <= 0:
            return
        # 调整水平移动距离
        self.rect.x += args[1] * self.speed
        self.rect.y += args[2] * self.speed
        # 限定在游戏窗口内部移动
        self.rect.x = 0 if self.rect.x < 0 else self.rect.x
        if self.rect.right > SCREEN_RECT.right:
            self.rect.rightc = SCREEN_RECT.right
        self.rect.y = 0 if self.rect.y < 0 else self.rect.y
        if self.rect.bottom > SCREEN_RECT.bottom:
            self.rect.bottom = SCREEN_RECT.bottom

    def blowup(self, enemies_group):
        """引爆炸弹
        :param enemies_group:敌机精灵组
        :return: 累计得分
        """
        # 如果没有足够数量的炸弹或者英雄飞机被摧毁，直接返回
        if self.bomb_count <= 0 or self.hp <= 0:
            return 0
        self.bomb_count -= 1
        score = 0
        count = 0
        # 遍历敌机精灵组，将窗口内的敌机引爆
        for enemy in enemies_group.sprites():
            # 判断敌机是否进入游戏窗口
            if enemy.rect.bottom > 0:
                score += enemy.value
                count += 1
                enemy.hp = 0
        print("炸毁了 %d 架敌机，得分 %d" % (count, score))
        return score

    def reset_plane(self):
        """ 重置飞机 """
        # 调用父类方法重置图像相关属性
        super().reset_plane()
        self.is_power = True
        self.bomb_count = HERO_BOMB_COUNT
        self.bullets_kind = 0
        # 发布英雄牺牲事件
        pygame.event.post(pygame.event.Event(HERO_DEAD_EVENT))
        # 设置 3s之后取消无敌定时器事件
        pygame.time.set_timer(HERO_POWER_OFF_EVENT, 3000)



class Bullet(GameSprite):
    """ 子弹类 """
    def __init__(self, kind, *group):
        """构造方法
        :param kind:子弹类型
        :param group: 要添加到的精灵组
        """
        image_name = ""